//----------------------------------------------------------------------------
//
//  TSDuck - The MPEG Transport Stream Toolkit
//  Copyright (c) 2005-2022, Thierry Lelegard
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//
//  1. Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
//  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
//  THE POSSIBILITY OF SUCH DAMAGE.
//
//----------------------------------------------------------------------------

#include "tsjsonOutputArgs.h"
#include "tsArgs.h"
#include "tsjsonObject.h"
#include "tsjsonRunningDocument.h"


//----------------------------------------------------------------------------
// Constructors and destructors.
//----------------------------------------------------------------------------

ts::json::OutputArgs::OutputArgs(bool use_short_opt, const UString& help) :
    _use_short_opt(use_short_opt),
    _json_help(help.empty() ? u"Report in JSON output format (useful for automatic analysis)." : help),
    _json_opt(false),
    _json_line(false),
    _json_udp(false),
    _line_prefix(),
    _udp_destination(),
    _udp_local(),
    _udp_ttl(0),
    _sock()
{
}


//----------------------------------------------------------------------------
// Define command line options in an Args.
//----------------------------------------------------------------------------

void ts::json::OutputArgs::defineArgs(Args& args)
{
    args.option(u"json", _use_short_opt ? 'j' : 0);
    args.help(u"json", _json_help);

    args.option(u"json-line", 0, Args::STRING, 0, 1, 0, Args::UNLIMITED_VALUE, true);
    args.help(u"json-line", u"'prefix'",
              u"Same as --json but report the JSON text as one single line in the message logger instead of the output file. "
              u"The optional string parameter specifies a prefix to prepend on the log "
              u"line before the JSON text to locate the appropriate line in the logs.");

    args.option(u"json-udp", 0, Args::STRING);
    args.help(u"json-udp", u"address:port",
              u"Same as --json but report the JSON text as one single line in a UDP datagram instead of the output file. "
              u"The 'address' specifies an IP address which can be either unicast or multicast. "
              u"It can be also a host name that translates to an IP address. "
              u"The 'port' specifies the destination UDP port.");

    args.option(u"json-udp-local", 0, Args::STRING);
    args.help(u"json-udp-local", u"address",
              u"With --json-udp, when the destination is a multicast address, specify "
              u"the IP address of the outgoing local interface. It can be also a host "
              u"name that translates to a local address.");

    args.option(u"json-udp-ttl", 0, Args::POSITIVE);
    args.help(u"json-udp-ttl",
              u"With --json-udp, specifies the TTL (Time-To-Live) socket option. "
              u"The actual option is either \"Unicast TTL\" or \"Multicast TTL\", "
              u"depending on the destination address. Remember that the default "
              u"Multicast TTL is 1 on most systems.");
}


//----------------------------------------------------------------------------
// Load arguments from command line.
// Args error indicator is set in case of incorrect arguments
//----------------------------------------------------------------------------

bool ts::json::OutputArgs::loadArgs(DuckContext& duck, Args& args)
{
    bool ok = true;
    _json_opt = args.present(u"json");
    _json_line = args.present(u"json-line");
    _json_udp = args.present(u"json-udp");
    args.getValue(_line_prefix, u"json-line");
    args.getIntValue(_udp_ttl, u"json-udp-ttl");
    _udp_destination.clear();
    _udp_local.clear();
    if (_json_udp) {
        ok = _udp_destination.resolve(args.value(u"json-udp"), args);
    }
    if (args.present(u"json-udp-local")) {
        ok = _udp_local.resolve(args.value(u"json-udp-local"), args) && ok;
    }
    return ok;
}


//----------------------------------------------------------------------------
// Issue a JSON report, except --json file.
//----------------------------------------------------------------------------

bool ts::json::OutputArgs::reportOthers(const json::Value& root, Report& rep)
{
    bool ok = true;

    if (_json_line || _json_udp) {
        // Generate one JSON line.
        TextFormatter text(rep);
        text.setString();
        text.setEndOfLineMode(TextFormatter::EndOfLineMode::SPACING);
        root.print(text);
        UString line;
        text.getString(line);

        // Report in logger.
        if (_json_line) {
            rep.info(_line_prefix + line);
        }

        // Report in UDP.
        if (_json_udp) {
            // Open socket the first time.
            if (!_sock.isOpen()) {
                // Create UDP socket.
                ok = _sock.open(rep) &&
                     _sock.setDefaultDestination(_udp_destination, rep) &&
                     (!_udp_local.hasAddress() || _sock.setOutgoingMulticast(_udp_local, rep)) &&
                     (_udp_ttl <= 0 || _sock.setTTL(_udp_ttl, rep));
            }
            if (ok) {
                std::string line8;
                line.toUTF8(line8);
                ok = _sock.send(line8.data(), line8.size(), rep);
            }
        }
    }

    return ok;
}


//----------------------------------------------------------------------------
// Issue a JSON report according to options.
//----------------------------------------------------------------------------

bool ts::json::OutputArgs::report(const json::Value& root, std::ostream& stm, Report& rep)
{
    // Process file output.
    if (_json_opt) {
        TextFormatter text(rep);
        text.setStream(stm);
        root.print(text);
        text << ts::endl;
        text.close();
    }

    // Other output forms.
    return reportOthers(root, rep);
}

bool ts::json::OutputArgs::report(const json::Value& root, json::RunningDocument& doc, Report& rep)
{
    // Process file output.
    if (_json_opt) {
        doc.add(root);
    }

    // Other output forms.
    return reportOthers(root, rep);
}
